package logic

import (
	"common"
	"common/biz"
	"common/logs"
	"common/utils"
	"context"
	"core/models/entity"
	"core/repo"
	"core/service"
	"fmt"
	"framework/msError"
	"framework/remote"
	"hall/component/proto"
	"hall/models/request"
	"hall/models/response"
	"sync"
	"time"
)

const (
	UserWebTime = 10 * 60 * time.Second
)

type InvestmentManager struct {
	sync.RWMutex
	manager          *Manager
	users            map[uint]*entity.User
	usersTimer       map[uint]*time.Timer
	exchangeCh       chan *proto.InvestmentExchange
	warehouseService *service.WarehouseService
	userService      *service.UserService
}

func (m *InvestmentManager) run() {
	//兑换处理
	go m.exchangeChHandler()
}

func (m *InvestmentManager) AddUser(data *entity.User) {
	m.Lock()
	defer m.Unlock()
	_, ok := m.users[data.Uid]
	m.users[data.Uid] = data
	if ok {
		//重启用户心跳
		m.usersTimer[data.Uid].Reset(UserWebTime)
		return
	}
	//添加用户心跳
	go m.scheduleUserTimerHandle(data)
}

func (m *InvestmentManager) scheduleUserTimerHandle(data *entity.User) {
	m.Lock()
	defer m.Unlock()
	if m.usersTimer[data.Uid] != nil {
		m.usersTimer[data.Uid].Stop()
	}
	m.usersTimer[data.Uid] = time.AfterFunc(UserWebTime, func() {
		m.usersTimer[data.Uid].Stop()
		delete(m.usersTimer, data.Uid)
		delete(m.users, data.Uid)
	})
}

func (m *InvestmentManager) Exchange(ctx context.Context, user *entity.User, req *request.InvestmentExchange, session *remote.Session) *msError.Error {
	m.AddUser(user)
	open := m.userService.GetConfInfo(ctx, "open_center")
	if open != "1" {
		logs.Info("[marketHandler]用户%d InvestmentExchange err:未开启 req=%v", user.Uid, req)
		return biz.FunctionOff
	}
	data := m.warehouseService.GetInvestmentCore(ctx, user.Uid)
	var consume float64
	var info response.InvestmentInfo
	exNum := req.Num
	if req.TypeID == data.One.TypeID {
		info = data.One
		if float64(exNum) > data.Two.Num {
			logs.Info("[marketHandler]用户%d InvestmentExchange err:材料不足 req=%v", user.Uid, req)
			return biz.GoodsPointsError
		}
		consume = utils.DecimalFloatMul(data.Two.Rate, utils.DecimalIntByFloat(req.Num))
	} else if req.TypeID == data.Two.TypeID {
		info = data.Two
		exNum = utils.DecimalFloatByInt(utils.DecimalFloatMul(utils.DecimalIntByFloat(exNum), data.Two.Rate))
		if float64(req.Num) > data.One.Num {
			logs.Info("[marketHandler]用户%d InvestmentExchange err:材料不足 req=%v", user.Uid, req)
			return biz.GoodsPointsError
		}
		//consume = utils.DecimalFloatDiv(utils.DecimalIntByFloat(req.Num), data.Two.Rate)
		consume = utils.DecimalIntByFloat(req.Num)
	}
	var num, total, fee float64
	var consumeInfo response.MaterialData
	for _, v := range data.My {
		if v.TypeID == req.TypeID {
			continue
		}
		num = v.Num
		consumeInfo = v
	}
	if req.TypeID == 11 {
		fee = utils.DecimalFloatMul(utils.DecimalIntByFloat(exNum), data.Fee)
	}
	total = utils.DecimalFloatSub(utils.DecimalIntByFloat(exNum), fee)
	if consume > num {
		logs.Info("[marketHandler]用户%d InvestmentExchange err:材料不足 req=%v", user.Uid, req)
		return biz.GoodsPointsError
	}
	ex := new(proto.InvestmentExchange)
	ex.Session = session
	ex.InvExchange = &entity.InvestmentExchangeLog{
		UID:             user.Uid,
		TypeID:          info.TypeID,
		TypeName:        info.TypeName,
		TypeImg:         info.TypeImg,
		Num:             exNum,
		Fee:             fee,
		Total:           total,
		ConsumeTypeID:   consumeInfo.TypeID,
		ConsumeTypeName: consumeInfo.TypeName,
		ConsumeTypeImg:  consumeInfo.TypeImg,
		ConsumeNum:      consume,
		AddTime:         time.Now().Format(time.DateTime),
	}
	m.exchangeCh <- ex
	return nil
}

func (m *InvestmentManager) exchangeChHandler() {
	for {

		select {
		case data, ok := <-m.exchangeCh:
			if ok {
				uidStr := []string{fmt.Sprint(data.InvExchange.UID)}
				ctx := context.TODO()
				err := m.warehouseService.InvExchange(ctx, data.InvExchange)
				if err != nil {
					m.manager.ServerMessagePush(uidStr, proto.AbnormalNotifyData(common.F(err)), data.Session)
					continue
				}
				go m.sendUsers(data.Session)
			}
		}
	}
}

func (m *InvestmentManager) sendUsers(session *remote.Session) {
	ctx := context.TODO()
	for _, v := range m.users {
		uidStr := []string{fmt.Sprint(v.Uid)}
		m.manager.ServerMessagePush(uidStr, proto.InvestmentNotifyData(m.warehouseService.GetInvestmentCore(ctx, v.Uid)), session)
	}
}

func (m *InvestmentManager) Out(uid uint) {
	m.Lock()
	defer m.Unlock()
	_, ok := m.users[uid]
	if ok {
		m.usersTimer[uid].Stop()
		delete(m.usersTimer, uid)
		delete(m.users, uid)
	}
}

func NewInvestmentManager(r *repo.Manager) *InvestmentManager {
	i := &InvestmentManager{
		manager:          NewManager(),
		users:            make(map[uint]*entity.User),
		usersTimer:       make(map[uint]*time.Timer),
		exchangeCh:       make(chan *proto.InvestmentExchange, 1024),
		warehouseService: service.NewWarehouseService(r),
		userService:      service.NewUserService(r),
	}
	i.run()
	return i
}
